/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-05-28     vandoul       the first version
 */
#include <rtthread.h>
#include <cJSON_Utils.h>
#include <stdio.h>

#include <drv_gpio.h>
#include <drivers/pin.h>
#include <nu_uart.h>

#include "srv_mb_rtu.h"
#include "srv_tcp.h"
#include "srv_param.h"
#include "srv_mqtt.h"

#define LOG_TAG           "SRV_TEST"
#define LOG_LVL           LOG_LVL_DBG
#include <ulog.h>

struct srv_test_param {
    char name[32];
    struct srv_param_app app;
};
static void modbus_rtu_custom_rts(modbus_t *ctx, int on)
{
    srv_mb_object_t mb_obj = srv_mb_rtu_get_object_from_modbus(ctx);
    int rts_pin = srv_mb_rtu_get_rts_pin(mb_obj);
    void *user_data = srv_mb_rtu_get_user_data(mb_obj);
    LOG_D("rts:%d, on:%d", rts_pin, on);
    if(on) {
        if(rts_pin != -1) {
            rt_pin_write(rts_pin, PIN_HIGH);
        }
    } else {
        if(user_data) {
            UART_WAIT_TX_EMPTY((UART_T *)user_data);
        }
        if(rts_pin != -1) {
            rt_pin_write(rts_pin, PIN_LOW);
        }
    }
}
static int srv_mb_rtu_init(struct srv_param_service *param)
{
    if((param == RT_NULL) || (param->type != SRV_PARAM_SERVICE_TYPE_MB_RTU)) {
        return -1;
    }
    struct srv_param_rtu *rtu = &param->rtu;
    struct srv_mb_config srv_mb_cfg;

    int id = 0;
    sscanf(rtu->path, "uart%d", &id);
    srv_mb_cfg.uart = rtu->path;
    srv_mb_cfg.baud = rtu->baud;
    srv_mb_cfg.data_bits = rtu->data_bits;
    srv_mb_cfg.parity = rtu->parity;
    srv_mb_cfg.stop_bits = rtu->stop_bits;
    srv_mb_cfg.rts_pin = rtu->rts_pin;
    srv_mb_cfg.custom_rts = modbus_rtu_custom_rts;
    srv_mb_cfg.priority = -1;

    void *user_data_list[10] = {
        UART0,UART1,UART2,UART3,UART4,
        UART5,UART6,UART7,UART8,UART9,
    };

    LOG_D("[%d] uart:%s,baud:%d,%d%c%d,rts_pin:%d,priority:%d"
            , id, srv_mb_cfg.uart
            , srv_mb_cfg.baud, srv_mb_cfg.data_bits, srv_mb_cfg.parity, srv_mb_cfg.stop_bits
            , srv_mb_cfg.rts_pin, srv_mb_cfg.priority);
    int srv_num = srv_mb_rtu_create(&srv_mb_cfg);
    if(srv_num < 0) {
        LOG_E("create mb_rtu srv failed!");
        return -1;
    }
    srv_mb_rtu_set_user_data(srv_num, user_data_list[id]);
    srv_mb_rtu_set_name(srv_num, param->name);
    srv_mb_rtu_start(srv_num);
    return 0;
}
static int srv_tcp_init(struct srv_param_service *param)
{
    if((param == RT_NULL) || (param->type != SRV_PARAM_SERVICE_TYPE_TCP)) {
        return -1;
    }
    struct srv_param_tcp *tcp = &param->tcp;
    struct srv_tcp_config tcp_config;
    tcp_config.priority = -1;
    int srv_num = srv_tcp_create(&tcp_config);
    if(srv_num < 0) {
        LOG_E("create tcp srv failed!");
        return -1;
    }
    srv_tcp_set_addr(srv_num, tcp->addr, tcp->port);
    srv_tcp_set_name(srv_num, param->name);
    srv_tcp_start(srv_num);
    return 0;
}
static int srv_mqtt_init(struct srv_param_service *param)
{
    if((param == RT_NULL) || (param->type != SRV_PARAM_SERVICE_TYPE_MQTT)) {
        return -1;
    }
    struct srv_param_mqtt *mqtt = &param->mqtt;
    struct srv_mqtt_config mqtt_config;
    rt_memcpy(mqtt_config.name, param->name, rt_strlen(param->name)+1);
    mqtt_config.url = mqtt->url;
    mqtt_config.prefix = mqtt->prefix;
    int srv_num = srv_mqtt_create(&mqtt_config);
    if(srv_num < 0) {
        LOG_E("create mqtt srv failed");
        return -1;
    }
    srv_mqtt_start(srv_num);
    return 0;
}
static int srv_get_srv_num(char *name)
{
    int id;
    id = srv_mb_rtu_get_srv_num(name);
    if(id < 0) {
        id = srv_tcp_get_srv_num(name);
        if(id < 0) {
            id = srv_mqtt_get_srv_num(name);
        }
    }
    return id;
}
struct srv_trans_data {
    int type;
    union {
        struct srv_mb_data mb_data;
        struct srv_tcp_data srv_data;
        struct srv_mqtt_data mqtt_data;
    };
};
static int srv_trans(int id, struct srv_trans_data *param)
{
    if(param->type == SRV_PARAM_SERVICE_TYPE_MB_RTU) {
        return srv_mb_rtu_trans(id, &param->mb_data);
    } else if(param->type == SRV_PARAM_SERVICE_TYPE_TCP) {
        return srv_tcp_trans(id, &param->srv_data);
    } else if(param->type == SRV_PARAM_SERVICE_TYPE_MQTT) {
        return srv_mqtt_trans(id, &param->mqtt_data);
    }
    return -1;
}
static int srv_fromrtu2totcp(struct srv_test_param *param, struct srv_trans_data *data, uint8_t *buff)
{
    if((param == RT_NULL) || (buff == RT_NULL)) {
        return -1;
    }
    struct srv_param_app *app = &param->app;
    uint8_t *io_buff = RT_NULL;
    uint16_t *reg_buff = RT_NULL;
    int len = 0;
    switch(app->function) {
    case SRV_MB_DATA_TYPE_READ_IO:
    case SRV_MB_DATA_TYPE_READ_INPUT:
    {
        io_buff = data->mb_data.rsp.io_buff;
    }
    break;
    case SRV_MB_DATA_TYPE_READ_INPUT_REG:
    case SRV_MB_DATA_TYPE_READ_HOLD_REG:
    {
        reg_buff = data->mb_data.rsp.reg_buff;
    }
    break;
    }
    if(data->type == SRV_PARAM_SERVICE_TYPE_MB_RTU) {
        len += rt_sprintf(buff+len, "{\"server\":\"%s\",\"name\":\"%s\",\"function\":%d,\"data\":[", param->name, app->name, app->function);
        for(int i=0; i<app->reg_num; i++) {
            if(i) {
                buff[len++] = ',';
            }
            if(reg_buff) {
                len += rt_sprintf(buff+len, "%d", reg_buff[i]);
            } else if(io_buff) {
                len += rt_sprintf(buff+len, "%d", io_buff[i]);
            }
        }
        len += rt_sprintf(buff+len, "]}");
        buff[len] = '\0';
    } else if(data->type == SRV_PARAM_SERVICE_TYPE_MB_TCP) {
        ;
    }
    return len;
}
static int srv_fromrtu2tomqtt(struct srv_test_param *param, struct srv_trans_data *data, uint8_t *buff)
{
    if((param == RT_NULL) || (buff == RT_NULL)) {
        return -1;
    }
    struct srv_param_app *app = &param->app;
    uint8_t *io_buff = RT_NULL;
    uint16_t *reg_buff = RT_NULL;
    int len = 0;
    switch(app->function) {
    case SRV_MB_DATA_TYPE_READ_IO:
    case SRV_MB_DATA_TYPE_READ_INPUT:
    {
        io_buff = data->mb_data.rsp.io_buff;
    }
    break;
    case SRV_MB_DATA_TYPE_READ_INPUT_REG:
    case SRV_MB_DATA_TYPE_READ_HOLD_REG:
    {
        reg_buff = data->mb_data.rsp.reg_buff;
    }
    break;
    }
    if(data->type == SRV_PARAM_SERVICE_TYPE_MB_RTU) {
        len += rt_sprintf(buff+len, "{\"function\":%d,\"data\":[", app->function);
        for(int i=0; i<app->reg_num; i++) {
            if(i) {
                buff[len++] = ',';
            }
            if(reg_buff) {
                len += rt_sprintf(buff+len, "%d", reg_buff[i]);
            } else if(io_buff) {
                len += rt_sprintf(buff+len, "%d", io_buff[i]);
            }
        }
        len += rt_sprintf(buff+len, "]}");
        buff[len] = '\0';
    } else if(data->type == SRV_PARAM_SERVICE_TYPE_MB_TCP) {
        ;
    }
    return len;
}
static uint8_t rsp_buff[1024];
static uint8_t req_buff[1024];
void srv_test_task(void *args)
{
    struct srv_test_param *param = (struct srv_test_param *)args;
    struct srv_param_app *app = &param->app;
    int from_type = app->from_type;
    int to_type = app->to_type;
    int from_id;
    int to_id;
    LOG_D("test task, from:%d", from_type);
    if(from_type == SRV_PARAM_SERVICE_TYPE_MB_RTU) {
        from_id = srv_mb_rtu_get_srv_num(app->from);
    } else if(from_type == SRV_PARAM_SERVICE_TYPE_MB_TCP) {
        rt_free(args);
        return ;
    } else {
        rt_free(args);
        return ;
    }
    LOG_D("%s from:%s,id:%d,type:%d", app->name, app->from, from_id, from_type);
    if(to_type == SRV_PARAM_SERVICE_TYPE_TCP) {
        to_id = srv_tcp_get_srv_num(app->to);
    } else if(to_type == SRV_PARAM_SERVICE_TYPE_MQTT) {
        to_id = srv_mqtt_get_srv_num(app->to);
    } else {
        rt_free(args);
        return ;
    }
    LOG_D("%s to:%s,id:%d,type:%d", app->name, app->to, to_id, to_type);
    struct srv_trans_data trans_data;
    rt_tick_t last_tick;
    while(1) {
        last_tick = rt_tick_get();
        if(from_type == SRV_PARAM_SERVICE_TYPE_MB_RTU) {
            trans_data.type = SRV_PARAM_SERVICE_TYPE_MB_RTU;
            trans_data.mb_data.addr = app->addr;
            trans_data.mb_data.func = app->function;
            trans_data.mb_data.reg = app->base_reg;
            trans_data.mb_data.timeout_s = 2;
            trans_data.mb_data.timeout_us = 0;
            trans_data.mb_data.req_num = app->reg_num;
            trans_data.mb_data.rsp.buff = rsp_buff;
            LOG_D("%s, addr:%d, func:%d, reg%d, req_num:%d", param->name, app->addr, app->function, app->base_reg, app->reg_num);
            if(srv_trans(from_id, &trans_data) == 0) {
                LOG_D("%s read ok", app->name);
            } else {
                LOG_E("%s read failed", app->name);
            }
        } else if(from_type == SRV_PARAM_SERVICE_TYPE_MB_TCP) {
            LOG_E("%s not support type!", app->name);
        } else {
            LOG_E("%s invalid from type!", app->name);
        }
        if(to_type == SRV_PARAM_SERVICE_TYPE_TCP) {
            int len = srv_fromrtu2totcp(param, &trans_data, req_buff);
            trans_data.type = SRV_PARAM_SERVICE_TYPE_TCP;
            trans_data.srv_data.addr = RT_NULL;
            trans_data.srv_data.port = -1;
            trans_data.srv_data.req_size = len;
            trans_data.srv_data.req.buff = req_buff;
            trans_data.srv_data.rsp_size = 0;
            trans_data.srv_data.rsp.buff = RT_NULL;
            trans_data.srv_data.timeout_s = 0;
            trans_data.srv_data.timeout_us = 0;
            if(0 == srv_trans(to_id, &trans_data)) {
                LOG_D("%s tcp send ok", app->name);
            } else {
                LOG_E("%s tcp send failed", app->name);
            }
        } else if(to_type == SRV_PARAM_SERVICE_TYPE_MQTT) {
            char topic[64];
            rt_sprintf(topic, "/%s/%s", param->name, app->name);
            int len = srv_fromrtu2tomqtt(param, &trans_data, req_buff);
            trans_data.type = SRV_PARAM_SERVICE_TYPE_MQTT;
            trans_data.mqtt_data.qos = 1;
            trans_data.mqtt_data.topic = topic;
            trans_data.mqtt_data.size = len;
            trans_data.mqtt_data.buff = req_buff;
            if(0 == srv_trans(to_id, &trans_data)) {
                LOG_D("%s mqtt send ok", app->name);
            } else {
                LOG_E("%s mqtt send failed", app->name);
            }
        } else {
            LOG_E("%s invalid to type!", app->name);
        }
        rt_thread_delay_until(&last_tick, rt_tick_from_millisecond(1000));
    }
    rt_free(args);
}
#include <finsh.h>
static int srv_test(int argc, char *argv[])
{
    if(argc == 1) {
        rt_kprintf("Usage: %s <param>\r\n", argv[0]);
        return -1;
    }
    struct srv_param *param = srv_param_config_load(argv[1]);
    LOG_D("param: 0x%p", param);
    LOG_D("srv_num:%d", param->srv_num);
    for(int i=0; i<param->srv_num; i++) {
        struct srv_param_service *srv = &param->srv_list[i];
        if(srv->type == SRV_PARAM_SERVICE_TYPE_MB_RTU) {
            LOG_D("mb_rtu init.%s,%d,%s", srv->name, srv->type, srv->rtu.path);
            srv_mb_rtu_init(srv);
        } else if(srv->type == SRV_PARAM_SERVICE_TYPE_TCP) {
            LOG_D("tcp init.%s,%d,%s", srv->name, srv->type, srv->tcp.addr);
            srv_tcp_init(srv);
        } else if(srv->type == SRV_PARAM_SERVICE_TYPE_MQTT) {
            LOG_D("mqtt init.%s,%d,%s", srv->name, srv->type, srv->mqtt.url);
            srv_mqtt_init(srv);
        }
    }
    //
    LOG_D("app_num:%d", param->app_num);
    for(int i=0; i<param->app_num; i++) {
        struct srv_param_app *app = &param->app_list[i];
        struct srv_test_param *new_param = rt_malloc(sizeof(struct srv_test_param));
        if(new_param == RT_NULL) {
            LOG_E("malloc test param %d failed!", i);
            break;
        }
        rt_memcpy(&new_param->app, app, sizeof(struct srv_param_app));
        rt_memcpy(new_param->name, param->name, 32);
        char name[32];
        rt_sprintf(name, "srv_test%d", i);
        rt_err_t ret = RT_EOK;
        rt_thread_t tid = rt_thread_create(name, srv_test_task, new_param, 2048, 6, 10);
        if(tid != RT_NULL) {
            ret |= rt_thread_startup(tid);
        } else {
            LOG_E("create app %s failed", name);
        }
        if(ret != RT_EOK) {
            LOG_E("start app %s failed! %d", name, ret);
            if(tid) {
                rt_thread_delete(tid);
                tid = RT_NULL;
            }
            rt_free(new_param);
            new_param = RT_NULL;
        }
    }
    //delete
    rt_free(param);
    param = RT_NULL;
    return 0;
}
MSH_CMD_EXPORT(srv_test, srv test.);
